import { clone, isString } from '@antv/util';

//G6.Util.processParallelEdges processes the edges with same source node and target node,
// on this basis, processParallelEdgesOnAnchorPoint consider the end nodes and anchor points in the same time.
export const processParallelEdgesOnAnchorPoint = (
  edges,
  offsetDiff = 15,
  multiEdgeType = 'cubic-vertical',
  singleEdgeType = undefined,
  loopEdgeType = undefined
) => {
  const len = edges.length;
  const cod = offsetDiff * 2;
  const loopPosition = [
    'top',
    'top-right',
    'right',
    'bottom-right',
    'bottom',
    'bottom-left',
    'left',
    'top-left',
  ];
  const edgeMap = {};
  const tags = [];
  const reverses = {};
  for (let i = 0; i < len; i++) {
    const edge = edges[i];
    const { source, target, sourceAnchor, targetAnchor } = edge;
    const sourceTarget = `${source}|${sourceAnchor}-${target}|${targetAnchor}`;

    if (tags[i]) { continue; }
    if (!edgeMap[sourceTarget]) {
      edgeMap[sourceTarget] = [];
    }
    tags[i] = true;
    edgeMap[sourceTarget].push(edge);
    for (let j = 0; j < len; j++) {
      if (i === j) { continue; }
      const sedge = edges[j];
      const {
        source: src,
        target: dst,
        sourceAnchor: srcAnchor,
        targetAnchor: dstAnchor,
      } = sedge;

      // 两个节点之间共同的边
      // 第一条的source = 第二条的target
      // 第一条的target = 第二条的source
      if (!tags[j]) {
        if (
          source === dst &&
          sourceAnchor === dstAnchor &&
          target === src &&
          targetAnchor === srcAnchor
        ) {
          edgeMap[sourceTarget].push(sedge);
          tags[j] = true;
          reverses[
            `${src}|${srcAnchor}|${dst}|${dstAnchor}|${edgeMap[sourceTarget].length - 1
            }`
          ] = true;
        } else if (
          source === src &&
          sourceAnchor === srcAnchor &&
          target === dst &&
          targetAnchor === dstAnchor
        ) {
          edgeMap[sourceTarget].push(sedge);
          tags[j] = true;
        }
      }
    }
  }
  const keys = Object.keys(edgeMap);
  for (const key of keys) {
    const arcEdges = edgeMap[key];
    const { length } = arcEdges;
    for (let k = 0; k < length; k++) {
      const current = arcEdges[k];
      if (current.source === current.target) {
        if (loopEdgeType) { current.type = loopEdgeType; }
        // 超过8条自环边，则需要重新处理
        current.loopCfg = {
          position: loopPosition[k % 8],
          dist: Math.floor(k / 8) * 20 + 50,
        };
        continue;
      }
      if (
        length === 1 &&
        singleEdgeType &&
        (current.source !== current.target ||
          current.sourceAnchor !== current.targetAnchor)
      ) {
        current.type = singleEdgeType;
        continue;
      }
      console.log(current);
      if (current.connectType === 'EXECUTE_CHAIN') {
        console.log('===========FIND EXECUTE CHAIN====');
        current.type = 'line-dash';
      } else if(current.connectType === 'INIT_DU_OUT_POINT'){
        console.log('===========FIND VARCHAIN CHAIN====');

        current.type = multiEdgeType;

        if(current.style){
          Object.assign(current.style,{stroke: '#2ecc71'});
        }else{
          current.style = {stroke: '#2ecc71'};
        }

      }else {
        current.type = multiEdgeType;
      }
      const sign =
        (k % 2 === 0 ? 1 : -1) *
        (reverses[
          `${current.source}|${current.sourceAnchor}|${current.target}|${current.targetAnchor}|${k}`
        ]
          ? -1
          : 1);
      if (length % 2 === 1) {
        current.curveOffset = sign * Math.ceil(k / 2) * cod;
      } else {
        current.curveOffset = sign * (Math.floor(k / 2) * cod + offsetDiff);
      }
    }
  }
  return edges;
};


export const undo = (graph: any) => {

  const undoStack = graph.getUndoStack();
  if (!undoStack || undoStack.length === 1) {
    return;
  }

  const currentData = undoStack.pop();
  console.log(currentData);
  if (currentData) {
    const { action } = currentData;
    graph.pushStack(action, clone(currentData.data), 'redo');
    let data = currentData.data.before;

    if (action === 'add') {
      data = currentData.data.after;
    }

    if (!data) { return; }

    switch (action) {
      case 'visible': {
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            const item = graph.findById(model.id);
            if (model.visible) {
              graph.showItem(item, false);
            } else {
              graph.hideItem(item, false);
            }
          });
        });
        break;
      }
      case 'render':
      case 'update':
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            const item = graph.findById(model.id);
            delete model.id;
            if (item) {
              graph.updateItem(item, model, false);
              if (item.getType() === 'combo') { graph.updateCombo(item as any); };
            }
          });
        });
        break;
      case 'changedata':
        graph.changeData(data, false);
        break;
      case 'delete': {
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            const itemType = model.itemType;
            delete model.itemType;
            graph.addItem(itemType, model, false);
          });
        });
        break;
      }
      case 'add':
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            graph.removeItem(model.id, false);
          });
        });
        break;
      case 'updateComboTree':
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            graph.updateComboTree(model.id, model.parentId, false);
          });
        });
        break;
      case 'layout':
        graph.updateLayout(data, undefined, undefined, false);
        break;
      default:
    }
  }
};



export const redo = (graph: any) => {

  const redoStack = graph.getRedoStack();

  if (!redoStack || redoStack.length === 0) {
    return;
  }

  const currentData = redoStack.pop();
  if (currentData) {
    const { action } = currentData;
    let data = currentData.data.after;
    graph.pushStack(action, clone(currentData.data));
    if (action === 'delete') {
      data = currentData.data.before;
    }

    if (!data) { return; }

    switch (action) {
      case 'visible': {
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            const item = graph.findById(model.id);
            if (model.visible) {
              graph.showItem(item, false);
            } else {
              graph.hideItem(item, false);
            }
          });
        });
        break;
      }
      case 'render':
      case 'update':
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            const item = graph.findById(model.id);
            delete model.id;
            if (item) {
              graph.updateItem(item, model, false);
              if (item.getType() === 'combo') { graph.updateCombo(item as any); };
            }
          });
        });
        break;
      case 'changedata':
        graph.changeData(data, false);
        break;
      case 'delete':
        if (data.edges) {
          data.edges.forEach((model) => {
            graph.removeItem(model.id, false);
          });
        }
        if (data.nodes) {
          data.nodes.forEach((model) => {
            graph.removeItem(model.id, false);
          });
        }
        if (data.combos) {
          data.combos.forEach((model) => {
            graph.removeItem(model.id, false);
          });
        }
        break;
      case 'add': {
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            const itemType = model.itemType;
            delete model.itemType;
            graph.addItem(itemType, model, false);
          });
        });
        break;
      }
      case 'updateComboTree':
        Object.keys(data).forEach((key) => {
          const array = data[key];
          if (!array) { return; }
          array.forEach((model) => {
            graph.updateComboTree(model.id, model.parentId, false);
          });
        });
        break;
      case 'layout':
        graph.updateLayout(data, undefined, undefined, false);
        break;
      default:
    }
  }
};

const DELTA = 0.05;
export const zoomIn = (graph: any) => {
  const currentZoom = graph.getZoom();
  const ratioOut = 1 / (1 - DELTA * 2);
  const maxZoom = graph.get('maxZoom');
  if (ratioOut * currentZoom > maxZoom) {
    return;
  }
  graph.zoomTo(currentZoom * ratioOut);
};

export const zoomOut = (graph: any) => {
  const currentZoom = graph.getZoom();
  const ratioIn = 1 - DELTA * 2;
  const minZoom = graph.get('minZoom');
  if (ratioIn * currentZoom < minZoom) {
    return;
  }
  graph.zoomTo(currentZoom * ratioIn);
};
